package test.com.jse;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

public class JsApi {

    public static void main(String[] args) throws URISyntaxException, InterruptedException, java.io.IOException {
        String mchId = "your_merchant_id"; // 商户ID
        String apiKey = "your_api_key"; // API密钥
        String nonceStr = generateNonceStr(); // 随机字符串
        String body = "商品描述"; // 商品描述
        String detail = ""; // 详细商品描述
        String outTradeNo = "your_out_trade_no"; // 商户订单号
        String totalFee = "1"; // 总金额（单位：分）
        String spbillCreateIp = "127.0.0.1"; // 客户端IP
        String notifyUrl = "http://notify.yourdomain.com/wechatpay"; // 回调地址
        String tradeType = "JSAPI"; // 交易类型
        String openId = "your_openid"; // 用户OpenID
        String privateKeyPem = "your_private_key_pem"; // 商户私钥PEM格式
        String certificateSerialNo = "your_certificate_serial_no"; // 证书序列号

        // Prepare the request parameters
        Map<String, String> params = new HashMap<>();
        params.put("appid", "your_appid"); // App ID
        params.put("mch_id", mchId);
        params.put("nonce_str", nonceStr);
        params.put("body", body);
        params.put("detail", detail);
        params.put("out_trade_no", outTradeNo);
        params.put("total_fee", totalFee);
        params.put("spbill_create_ip", spbillCreateIp);
        params.put("notify_url", notifyUrl);
        params.put("trade_type", tradeType);
        params.put("openid", openId);

        // Generate the signature
        String token = getToken("POST", "https://api.mch.weixin.qq.com/pay/unifiedorder", serializeParams(params), privateKeyPem, certificateSerialNo, nonceStr);

        // Prepare the URL with query parameters
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

        // Convert the parameters to a JSON object
        String jsonParams = params.entrySet().stream()
                .filter(entry -> !"sign".equals(entry.getKey()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                        (oldValue, newValue) -> oldValue, LinkedHashMap::new))
                .entrySet().stream()
                .map(entry -> "\"" + entry.getKey() + "\": \"" + entry.getValue() + "\"")
                .collect(Collectors.joining(", ", "{", "}"));

        // Sign the request
        String bodyContent = jsonParams + ",\"token\": \"" + token + "\"";

        // Create the HttpClient and HttpRequest
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI(url))
                .header("Content-Type", "application/xml")
                .POST(HttpRequest.BodyPublishers.ofString(
                        "<xml>" + bodyContent.replaceAll("\"", "") + "</xml>",
                        StandardCharsets.UTF_8))
                .build();

        // Send the request and get the response
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println("Status code: " + response.statusCode());
        System.out.println("Response body: " + response.body());

        if (response.statusCode() == 200) {
            parseResponse(response.body());
        } else {
            System.out.println("Call failed.");
        }
    }

    private static String generateNonceStr() {
        // Generate a random nonce string
        return Long.toHexString(System.currentTimeMillis());
    }

    private static String getToken(String method, String url, String body, String privateKeyPem, String certificateSerialNo, String nonceStr) {
        String schema = "WECHATPAY2-SHA256-RSA2048";
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(method, url, timestamp, nonceStr, body);
        String signature = sign(message.getBytes(StandardCharsets.UTF_8), privateKeyPem);
        String mchId="";
        return "mchid=\"" + mchId + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + certificateSerialNo + "\","
                + "signature=\"" + signature + "\"";
    }

    private static String sign(byte[] message, String privateKeyPem) {
        try {
            // Remove the BEGIN and END lines from the PEM format
            String privateKeyString = privateKeyPem.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            byte[] encoded = Base64.getDecoder().decode(privateKeyString);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = kf.generatePrivate(keySpec);

            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(privateKey);
            signature.update(message);
            byte[] signedBytes = signature.sign();

            return Base64.getEncoder().encodeToString(signedBytes);
        } catch (Exception e) {
            throw new RuntimeException("Failed to sign the request", e);
        }
    }

    private static String buildMessage(String method, String url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url;//.encodedPath();
//        if (url.encodedQuery() != null) {
//            canonicalUrl += "?" + url.encodedQuery();
//        }
        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }

    private static String serializeParams(Map<String, String> params) {
        return params.entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .map(entry -> entry.getKey() + "=" + entry.getValue())
                .collect(Collectors.joining("&"));
    }

    private static void parseResponse(String responseBody) {
        // Parse the XML response and extract the prepay_id
        // You can use a library like DOM or SAX for parsing XML
        // For simplicity, we will just print the response body
        System.out.println("Response body: " + responseBody);
    }
}